package app.log;


import app.sys.data.entity.SysLog;
import app.sys.data.entity.UserInfo;
import app.sys.data.enums.LogTypeEnum;
import app.sys.data.enums.OperateTypeEnum;
import app.sys.service.SysLogService;
import app.sys.utils.UserUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @author fyy
 * @date 2023/4/28 12:33
 * @description:
 */
@Aspect
@Component
public class SysLogAspect {
    private static final Logger logger = LoggerFactory.getLogger(SysLogAspect.class);

    @Autowired
    private SysLogService sysLogService;

    /**
     * 也可以通过切点表达式直接指定需要拦截的package,需要拦截的class 以及 method
     * 切点表达式:   execution(...)
     * <p>
     * execution(public * *(..)) 任意的公共方法
     * execution（* set*（..）） 以set开头的所有的方法
     * execution（* com.LoggerApply.*（..））com.LoggerApply这个类里的所有的方法
     * execution（* com.annotation.*.*（..））com.annotation包下的所有的类的所有的方法
     * execution（* com.annotation..*.*（..））com.annotation包及子包下所有的类的所有的方法
     * execution(* com.annotation..*.*(String,?,Long)) com.annotation包及子包下所有的类的有三个参数，第一个参数为String类型，第二个参数为任意类型，第三个参数为Long类型的方法
     */
    @Pointcut("@annotation(app.log.SysControllerLog)")
    public void logPointCut() {
    }

    /**
     * 环绕通知 @Around  ， 当然也可以使用 @Before (前置通知)  @After (后置通知)
     *
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        Object result = point.proceed();
        long time = System.currentTimeMillis() - beginTime;
        try {
            saveLog(point, time);
        } catch (Exception e) {
        }
        return result;
    }

    /**
     * 保存日志
     */
    private void saveLog(ProceedingJoinPoint joinPoint, long time) throws ClassNotFoundException {
        try {
            String targetName = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            Object[] arguments = joinPoint.getArgs();
            Class targetClass = Class.forName(targetName);
            Method[] methods = targetClass.getMethods();
            for (Method method : methods) {
                if (method.getName().equals(methodName)) {
                    Class[] clazzs = method.getParameterTypes();
                    if (clazzs.length == arguments.length) {
                        SysControllerLog annotation = method.getAnnotation(SysControllerLog.class);
                        if (annotation != null) {
                            String logContent = annotation.logContent();
                            LogTypeEnum type = annotation.logType();
                            OperateTypeEnum operate = annotation.operateType();
                            SysLog sysLog = new SysLog();
                            sysLog.setLogType(type.getKey()).setOperateType(operate.getKey()).setLogContent(logContent);
                            UserInfo loginUser = UserUtils.getLoginUser();
                            sysLog.setCreateUser(loginUser.getUserCode());
                            sysLogService.save(sysLog);
                        }
                    }
                }
            }
        } catch (Exception e1) {
            logger.error("----------操作请求日志记录失败----------" + e1);
        }
    }
}
